Update event emulation to handle offscreen children
authorAlexander Larsson <alexl@redhat.com>
Mon, 8 Jun 2009 13:57:59 +0000 (15:57 +0200)
committerAlexander Larsson <alexl@redhat.com>
Mon, 8 Jun 2009 17:42:34 +0000 (19:42 +0200)
We use the offscreen signals for getting parent, picking
children at a point and mapping coordinates between windows
embedding offscreens and offscreens.

This means we have two hierarchies more or less, one visible to apps via
the standard APIs and for drawing where the offscreens are their own
separate toplevels, and another one for event handling where embedded
offscreens appear as if they were children of the embedding window.

gdk/gdkdisplay.c
gdk/gdkoffscreenwindow.c
gdk/gdkwindow.c

index cb9d72a9aea2b5a1537e335994484eea0f91de26..e69d07ab934aca9fb656039773ce143d0a98ffd3 100644 (file)
@@ -906,7 +906,6 @@ synthesize_crossing_events (GdkDisplay *display,
     }
 }
 
-
 static void
 switch_to_pointer_grab (GdkDisplay *display,
                        GdkPointerGrabInfo *grab,
@@ -1074,9 +1073,33 @@ _gdk_display_pointer_grab_update (GdkDisplay *display,
     }
 }
 
-static gboolean
-is_parent_of (GdkWindow *parent,
-              GdkWindow *child)
+static GdkWindow *
+gdk_window_get_offscreen_parent (GdkWindow *window)
+{
+  GdkWindowObject *private = (GdkWindowObject *)window;
+  GdkWindow *res;
+
+  res = NULL;
+  g_signal_emit_by_name (private->impl_window,
+                        "get-offscreen-parent",
+                        &res);
+
+  return res;
+}
+
+/* Gets the toplevel for a window as used for events,
+   i.e. including offscreen parents */
+static GdkWindowObject *
+get_event_parent (GdkWindowObject *window)
+{
+  if (window->window_type ==GDK_WINDOW_OFFSCREEN)
+    return (GdkWindowObject *)gdk_window_get_offscreen_parent ((GdkWindow *)window);
+  else
+    return window->parent;
+}
+
+is_event_parent_of (GdkWindow *parent,
+                   GdkWindow *child)
 {
   GdkWindow *w;
 
@@ -1086,7 +1109,7 @@ is_parent_of (GdkWindow *parent,
       if (w == parent)
        return TRUE;
 
-      w = gdk_window_get_parent (w);
+      w = (GdkWindow *)get_event_parent ((GdkWindowObject *)w);
     }
 
   return FALSE;
@@ -1143,7 +1166,7 @@ _gdk_display_end_pointer_grab (GdkDisplay *display,
   grab = l->data;
   if (grab &&
       (if_child == NULL ||
-       is_parent_of (grab->window, if_child)))
+       is_event_parent_of (grab->window, if_child)))
     {
       grab->serial_end = serial;
       grab->implicit_ungrab = implicit;
index 5682b224856518d8926722d85ae1dcd649ece582..5289d71e92dc54a96149612f6e3b06188e045fef 100644 (file)
@@ -685,18 +685,6 @@ gdk_offscreen_window_reparent (GdkWindow *window,
   return was_mapped;
 }
 
-static gint
-gdk_offscreen_window_get_origin (GdkWindow *window,
-                                gint      *x,
-                                gint      *y)
-{
-  if (x)
-    *x = 0;
-  if (y)
-    *y = 0;
-
-  return TRUE;
-}
 
 static GdkWindow *
 get_offscreen_parent (GdkWindow *window)
@@ -730,6 +718,89 @@ from_parent (GdkWindow *window,
                         NULL);
 }
 
+static void
+to_parent (GdkWindow *window,
+          double offscreen_x, double offscreen_y,
+          double *parent_x, double *parent_y)
+{
+  GdkWindowObject *private;
+
+  private = (GdkWindowObject *)window;
+
+  g_signal_emit_by_name (private->impl_window,
+                        "to_parent",
+                        offscreen_x, offscreen_y,
+                        parent_x, parent_y,
+                        NULL);
+}
+
+static gint
+gdk_offscreen_window_get_origin (GdkWindow *window,
+                                gint      *x,
+                                gint      *y)
+{
+  GdkWindow *parent;
+  int tmpx, tmpy;
+
+  tmpx = 0;
+  tmpy = 0;
+
+  parent = get_offscreen_parent (window);
+  if (parent)
+    {
+      double dx, dy;
+      gdk_window_get_origin (parent,
+                            &tmpx, &tmpy);
+
+      to_parent (window,
+                0, 0,
+                &dx, &dy);
+      tmpx = floor (tmpx + dx + 0.5);
+      tmpy = floor (tmpy + dy + 0.5);
+    }
+
+
+  if (x)
+    *x = tmpx;
+  if (y)
+    *y = tmpy;
+
+  return TRUE;
+}
+
+static gint
+gdk_offscreen_window_get_deskrelative_origin (GdkWindow *window,
+                                             gint      *x,
+                                             gint      *y)
+{
+  GdkWindow *parent;
+  int tmpx, tmpy;
+
+  tmpx = 0;
+  tmpy = 0;
+
+  parent = get_offscreen_parent (window);
+  if (parent)
+    {
+      double dx, dy;
+      gdk_window_get_deskrelative_origin (parent,
+                                         &tmpx, &tmpy);
+
+      to_parent (window,
+                0, 0,
+                &dx, &dy);
+      tmpx = floor (tmpx + dx + 0.5);
+      tmpy = floor (tmpy + dy + 0.5);
+    }
+
+
+  if (x)
+    *x = tmpx;
+  if (y)
+    *y = tmpy;
+
+  return TRUE;
+}
 
 static gboolean
 gdk_offscreen_window_get_pointer (GdkWindow       *window,
@@ -1147,6 +1218,7 @@ gdk_offscreen_window_impl_iface_init (GdkWindowImplIface *iface)
   iface->queue_antiexpose = gdk_offscreen_window_queue_antiexpose;
   iface->queue_translation = gdk_offscreen_window_queue_translation;
   iface->get_origin = gdk_offscreen_window_get_origin;
+  iface->get_deskrelative_origin = gdk_offscreen_window_get_deskrelative_origin;
   iface->get_pointer = gdk_offscreen_window_get_pointer;
   iface->destroy = gdk_offscreen_window_destroy;
 }
index 76a9f82aeec15f8c45e3ddb9f4575edcc2ecf1e8..c8714d2b05134cbaf54758e64f9827980a11ee25 100644 (file)
@@ -312,7 +312,11 @@ static void do_move_region_bits_on_impl (GdkWindowObject *private,
                                         GdkRegion *region, /* In impl window coords */
                                         int dx, int dy);
 static void gdk_window_invalidate_in_parent (GdkWindowObject *private);
+static GdkWindow *gdk_window_get_offscreen_parent (GdkWindow *window);
 static void move_native_children (GdkWindowObject *private);
+static void update_cursor (GdkDisplay *display);
+static gboolean is_event_parent_of (GdkWindow *parent,
+                                   GdkWindow *child);
 
 static guint signals[LAST_SIGNAL] = { 0 };
 
@@ -387,6 +391,7 @@ gdk_window_init (GdkWindowObject *window)
   window->native_visibility = GDK_VISIBILITY_UNOBSCURED;
 }
 
+/* Stop and return on the first non-NULL parent */
 static gboolean
 accumulate_get_parent  (GSignalInvocationHint *ihint,
                         GValue                *return_accu,
@@ -395,7 +400,7 @@ accumulate_get_parent       (GSignalInvocationHint *ihint,
 {
   g_value_copy (handler_return, return_accu);
   /* Continue while returning NULL */
-  return g_value_get_pointer (handler_return) == NULL;
+  return g_value_get_object (handler_return) == NULL;
 }
 
 static GQuark quark_pointer_window = 0;
@@ -6808,34 +6813,6 @@ gdk_window_set_back_pixmap (GdkWindow *window,
     GDK_WINDOW_IMPL_GET_IFACE (private->impl)->set_back_pixmap (window, private->bg_pixmap);
 }
 
-static void
-update_cursor (GdkDisplay *display)
-{
-  GdkWindowObject *pointer_window, *cursor_window;
-  GdkPointerGrabInfo *grab;
-
-  pointer_window = (GdkWindowObject *)display->pointer_info.window_under_pointer;
-
-  cursor_window = pointer_window;
-  while (cursor_window->cursor == NULL &&
-        cursor_window->parent != NULL &&
-        cursor_window->parent->window_type != GDK_WINDOW_ROOT)
-    cursor_window = cursor_window->parent;
-
-  /* We ignore the serials here and just pick the last grab
-     we've sent, as that would shortly be used anyway. */
-  grab = _gdk_display_get_last_pointer_grab (display);
-  if (grab != NULL &&
-      !is_parent_of (grab->window, (GdkWindow *)cursor_window))
-    cursor_window = (GdkWindowObject *)grab->window;
-
-  /* Set all cursors on toplevel, otherwise its tricky to keep track of
-   * which native window has what cursor set. */
-  GDK_WINDOW_IMPL_GET_IFACE (pointer_window->impl)->set_cursor
-      (gdk_window_get_toplevel ((GdkWindow *)pointer_window),
-       cursor_window->cursor);
-}
-
 /**
  * gdk_window_set_cursor:
  * @window: a #GdkWindow
@@ -6870,7 +6847,7 @@ gdk_window_set_cursor (GdkWindow *window,
       if (cursor)
        private->cursor = gdk_cursor_ref (cursor);
 
-      if (is_parent_of (window, display->pointer_info.window_under_pointer))
+      if (is_event_parent_of (window, display->pointer_info.window_under_pointer))
        update_cursor (display);
     }
 }
@@ -7782,13 +7759,106 @@ gdk_window_redirect_free (GdkWindowRedirect *redirect)
   g_free (redirect);
 }
 
+/* Gets the toplevel for a window as used for events,
+   i.e. including offscreen parents */
+static GdkWindowObject *
+get_event_parent (GdkWindowObject *window)
+{
+  if (window->window_type ==GDK_WINDOW_OFFSCREEN)
+    return (GdkWindowObject *)gdk_window_get_offscreen_parent ((GdkWindow *)window);
+  else
+    return window->parent;
+}
+
+/* Gets the toplevel for a window as used for events,
+   i.e. including offscreen parents going up to the native
+   toplevel */
+static GdkWindow *
+get_event_toplevel (GdkWindow *w)
+{
+  GdkWindowObject *private = GDK_WINDOW_OBJECT (w);
+  GdkWindowObject *parent;
+
+  while ((parent = get_event_parent (private)) != NULL &&
+        (GDK_WINDOW_TYPE (parent) != GDK_WINDOW_ROOT))
+    private = parent;
+
+  return GDK_WINDOW (private);
+}
+
+static gboolean
+is_event_parent_of (GdkWindow *parent,
+                   GdkWindow *child)
+{
+  GdkWindow *w;
+
+  w = child;
+  while (w != NULL)
+    {
+      if (w == parent)
+       return TRUE;
+
+      w = (GdkWindow *)get_event_parent ((GdkWindowObject *)w);
+    }
+
+  return FALSE;
+}
+
+static void
+update_cursor (GdkDisplay *display)
+{
+  GdkWindowObject *pointer_window, *cursor_window, *parent, *toplevel;
+  GdkPointerGrabInfo *grab;
+
+  pointer_window = (GdkWindowObject *)display->pointer_info.window_under_pointer;
+
+  cursor_window = pointer_window;
+  while (cursor_window->cursor == NULL &&
+        (parent = get_event_parent (cursor_window)) != NULL &&
+        parent->window_type != GDK_WINDOW_ROOT)
+    cursor_window = parent;
+
+  /* We ignore the serials here and just pick the last grab
+     we've sent, as that would shortly be used anyway. */
+  grab = _gdk_display_get_last_pointer_grab (display);
+  if (grab != NULL &&
+      !is_event_parent_of (grab->window, (GdkWindow *)cursor_window))
+    cursor_window = (GdkWindowObject *)grab->window;
+
+  /* Set all cursors on toplevel, otherwise its tricky to keep track of
+   * which native window has what cursor set. */
+  toplevel = (GdkWindowObject *)get_event_toplevel ((GdkWindow *)pointer_window);
+  GDK_WINDOW_IMPL_GET_IFACE (toplevel->impl)->set_cursor
+    ((GdkWindow *)toplevel, cursor_window->cursor);
+}
+
+static void
+from_parent (GdkWindowObject *window,
+            double parent_x, double parent_y,
+            double *offscreen_x, double *offscreen_y)
+{
+  g_signal_emit_by_name (window,
+                        "from_parent",
+                        parent_x, parent_y,
+                        offscreen_x, offscreen_y,
+                        NULL);
+}
+
 static void
 convert_coords_to_child (GdkWindowObject *child,
                         double x, double y,
                         double *child_x, double *child_y)
 {
-  *child_x = x - child->x;
-  *child_y = y - child->y;
+  if (gdk_window_is_offscreen (child))
+    {
+      from_parent (child, x, y,
+                  child_x, child_y);
+    }
+  else
+    {
+      *child_x = x - child->x;
+      *child_y = y - child->y;
+    }
 }
 
 static gboolean
@@ -7796,7 +7866,7 @@ point_in_window (GdkWindowObject *window,
                 double x, double y)
 {
   return
-    x >= 0 &&  x < window->width &&
+    x >= 0 && x < window->width &&
     y >= 0 && y < window->height &&
     (window->shape == NULL ||
      gdk_region_point_in (window->shape,
@@ -7807,9 +7877,9 @@ point_in_window (GdkWindowObject *window,
 }
 
 static GdkWindow *
-convert_coords_to_toplevel (GdkWindow *window,
-                           double child_x, double child_y,
-                           double *toplevel_x, double *toplevel_y)
+convert_native_coords_to_toplevel (GdkWindow *window,
+                                  double child_x, double child_y,
+                                  double *toplevel_x, double *toplevel_y)
 {
   GdkWindowObject *private = (GdkWindowObject *)window;
   gdouble x, y;
@@ -7831,7 +7901,6 @@ convert_coords_to_toplevel (GdkWindow *window,
   return (GdkWindow *)private;
 }
 
-
 static void
 convert_toplevel_coords_to_window (GdkWindow *window,
                                   gdouble    toplevel_x,
@@ -7840,6 +7909,7 @@ convert_toplevel_coords_to_window (GdkWindow *window,
                                   gdouble   *window_y)
 {
   GdkWindowObject *private;
+  GdkWindowObject *parent;
   gdouble x, y;
   GList *children, *l;
 
@@ -7849,11 +7919,11 @@ convert_toplevel_coords_to_window (GdkWindow *window,
   y = toplevel_y;
 
   children = NULL;
-  while (private->parent != NULL &&
-        (GDK_WINDOW_TYPE (private->parent) != GDK_WINDOW_ROOT))
+  while ((parent = get_event_parent (private)) != NULL &&
+        (GDK_WINDOW_TYPE (parent) != GDK_WINDOW_ROOT))
     {
       children = g_list_prepend (children, private);
-      private = private->parent;
+      private = parent;
     }
 
   for (l = children; l != NULL; l = l->next)
@@ -7865,6 +7935,20 @@ convert_toplevel_coords_to_window (GdkWindow *window,
   *window_y = y;
 }
 
+static GdkWindowObject *
+pick_offscreen_child (GdkWindowObject *window,
+                     double x, double y)
+{
+  GdkWindowObject *res;
+
+  res = NULL;
+  g_signal_emit_by_name (window,
+                        "pick-offscreen-child",
+                        x, y, &res);
+
+  return res;
+}
+
 GdkWindow *
 _gdk_window_find_child_at (GdkWindow *window,
                           int x, int y)
@@ -7891,12 +7975,19 @@ _gdk_window_find_child_at (GdkWindow *window,
          if (point_in_window (sub, child_x, child_y))
            return (GdkWindow *)sub;
        }
+
+      if (private->has_offscreen_children)
+       {
+         sub = pick_offscreen_child (private,
+                                     x, y);
+         if (sub)
+           return (GdkWindow *)sub;
+       }
     }
 
   return NULL;
 }
 
-
 GdkWindow *
 _gdk_window_find_descendant_at (GdkWindow *toplevel,
                                double x, double y,
@@ -7906,6 +7997,7 @@ _gdk_window_find_descendant_at (GdkWindow *toplevel,
   GdkWindowObject *private, *sub;
   double child_x, child_y;
   GList *l;
+  gboolean found;
 
   private = (GdkWindowObject *)toplevel;
 
@@ -7913,6 +8005,7 @@ _gdk_window_find_descendant_at (GdkWindow *toplevel,
     {
       do
        {
+         found = FALSE;
          /* Children is ordered in reverse stack order, i.e. first is topmost */
          for (l = private->children; l != NULL; l = l->next)
            {
@@ -7929,11 +8022,24 @@ _gdk_window_find_descendant_at (GdkWindow *toplevel,
                  x = child_x;
                  y = child_y;
                  private = sub;
+                 found = TRUE;
                  break;
                }
            }
+         if (!found &&
+             private->has_offscreen_children)
+           {
+             sub = pick_offscreen_child (private,
+                                         x, y);
+             if (sub)
+               {
+                 found = TRUE;
+                 private = sub;
+                 from_parent (sub, x, y, &x, &y);
+               }
+           }
        }
-      while (l != NULL);
+      while (found);
     }
   else
     {
@@ -8041,14 +8147,14 @@ find_common_ancestor (GdkWindowObject *win1,
   while (tmp != NULL && tmp->window_type != GDK_WINDOW_ROOT)
     {
       path1 = g_list_prepend (path1, tmp);
-      tmp = tmp->parent;
+      tmp = get_event_parent (tmp);
     }
 
   tmp = win2;
   while (tmp != NULL && tmp->window_type != GDK_WINDOW_ROOT)
     {
       path2 = g_list_prepend (path2, tmp);
-      tmp = tmp->parent;
+      tmp = get_event_parent (tmp);
     }
 
   list1 = path1;
@@ -8289,7 +8395,7 @@ _gdk_syntesize_crossing_events (GdkDisplay                 *display,
            notify_type = GDK_NOTIFY_VIRTUAL;
 
          last = a;
-         win = a->parent;
+         win = get_event_parent (a);
          while (win != c && GDK_WINDOW_TYPE (win) != GDK_WINDOW_ROOT)
            {
              send_crossing_event (display, toplevel,
@@ -8303,7 +8409,7 @@ _gdk_syntesize_crossing_events (GdkDisplay                 *display,
                                   serial);
 
              last = win;
-             win = win->parent;
+             win = get_event_parent (win);
            }
        }
     }
@@ -8316,11 +8422,11 @@ _gdk_syntesize_crossing_events (GdkDisplay                 *display,
       if (c != b)
        {
          path = NULL;
-         win = b->parent;
+         win = get_event_parent (b);
          while (win != c && GDK_WINDOW_TYPE (win) != GDK_WINDOW_ROOT)
            {
              path = g_list_prepend (path, win);
-             win = win->parent;
+             win = get_event_parent (win);
            }
 
          if (non_linear)
@@ -8371,18 +8477,6 @@ _gdk_syntesize_crossing_events (GdkDisplay                 *display,
     }
 }
 
-static GdkWindow *
-get_toplevel (GdkWindow *w)
-{
-  GdkWindowObject *private = GDK_WINDOW_OBJECT (w);
-
-  while (private->parent != NULL &&
-        (GDK_WINDOW_TYPE (private->parent) != GDK_WINDOW_ROOT))
-    private = private->parent;
-
-  return GDK_WINDOW (private);
-}
-
 /* Returns the window inside the event window with the pointer in it
  * at the specified coordinates, or NULL if its not in any child of
  * the toplevel. It also takes into account !owner_events grabs.
@@ -8569,7 +8663,7 @@ _gdk_syntesize_crossing_events_for_geometry_change (GdkWindow *changed_window)
   display = gdk_drawable_get_display (changed_window);
 
   serial = _gdk_windowing_window_get_next_serial (display);
-  changed_toplevel = get_toplevel (changed_window);
+  changed_toplevel = get_event_toplevel (changed_window);
 
   if (changed_toplevel == display->pointer_info.toplevel_under_pointer)
     {
@@ -8642,7 +8736,7 @@ get_event_window (GdkDisplay                 *display,
          return (GdkWindow *)w;
        }
 
-      w = w->parent;
+      w = get_event_parent (w);
     }
 
   if (grab != NULL &&
@@ -8680,9 +8774,9 @@ proxy_pointer_event (GdkDisplay                 *display,
   gdk_event_get_coords (source_event, &toplevel_x, &toplevel_y);
   gdk_event_get_state (source_event, &state);
   time_ = gdk_event_get_time (source_event);
-  toplevel_window = convert_coords_to_toplevel (event_window,
-                                               toplevel_x, toplevel_y,
-                                               &toplevel_x, &toplevel_y);
+  toplevel_window = convert_native_coords_to_toplevel (event_window,
+                                                      toplevel_x, toplevel_y,
+                                                      &toplevel_x, &toplevel_y);
 
 
   /* If we get crossing events with subwindow unexpectedly being NULL
@@ -8845,6 +8939,7 @@ proxy_button_event (GdkEvent *source_event,
   GdkWindow *toplevel_window, *event_window;
   GdkWindow *event_win;
   GdkWindow *pointer_window;
+  GdkWindowObject *parent;
   GdkEvent *event;
   guint state;
   guint32 time_;
@@ -8859,9 +8954,9 @@ proxy_button_event (GdkEvent *source_event,
   gdk_event_get_state (source_event, &state);
   time_ = gdk_event_get_time (source_event);
   display = gdk_drawable_get_display (source_event->any.window);
-  toplevel_window = convert_coords_to_toplevel (event_window,
-                                               toplevel_x, toplevel_y,
-                                               &toplevel_x, &toplevel_y);
+  toplevel_window = convert_native_coords_to_toplevel (event_window,
+                                                      toplevel_x, toplevel_y,
+                                                      &toplevel_x, &toplevel_y);
 
   if (type == GDK_BUTTON_PRESS &&
       _gdk_display_has_pointer_grab (display, serial) == NULL)
@@ -8873,11 +8968,13 @@ proxy_button_event (GdkEvent *source_event,
 
       /* Find the event window, that gets the grab */
       w = (GdkWindowObject *)pointer_window;
-      while (w != NULL && w->parent->window_type != GDK_WINDOW_ROOT)
+      while (w != NULL &&
+            (parent = get_event_parent (w)) != NULL &&
+            parent->window_type != GDK_WINDOW_ROOT)
        {
          if (w->event_mask & GDK_BUTTON_PRESS_MASK)
            break;
-         w = w->parent;
+         w = parent;
        }
       pointer_window = (GdkWindow *)w;
 
@@ -9137,7 +9234,7 @@ _gdk_windowing_got_event (GdkDisplay *display,
   old_button = display->pointer_info.button;
 
   gdk_event_get_coords (event, &x, &y);
-  convert_coords_to_toplevel (event_window, x, y,  &x, &y);
+  convert_native_coords_to_toplevel (event_window, x, y,  &x, &y);
   display->pointer_info.toplevel_x = x;
   display->pointer_info.toplevel_y = y;
   gdk_event_get_state (event, &display->pointer_info.state);
@@ -9215,7 +9312,7 @@ get_extension_event_window (GdkDisplay                 *display,
       if (evmask & type_masks[type])
        return (GdkWindow *)w;
 
-      w = w->parent;
+      w = get_event_parent (w);
     }
 
   if (grab != NULL &&
@@ -9249,9 +9346,9 @@ _gdk_window_get_input_window_for_event (GdkWindow *native_window,
   toplevel_y = y;
 
   display = gdk_drawable_get_display (native_window);
-  toplevel_window = convert_coords_to_toplevel (native_window,
-                                               toplevel_x, toplevel_y,
-                                               &toplevel_x, &toplevel_y);
+  toplevel_window = convert_native_coords_to_toplevel (native_window,
+                                                      toplevel_x, toplevel_y,
+                                                      &toplevel_x, &toplevel_y);
   pointer_window = get_pointer_window (display, toplevel_window,
                                       toplevel_x, toplevel_y, serial);
   event_win = get_extension_event_window (display,